/**
 * @license BSD
 * @copyright 2014-2022 hizzgdev@163.com
 *
 * Project Home:
 *   https://github.com/hizzgdev/jsmind/
 */

if (!jsMind) {
    throw new Error('jsMind is not defined');
}

const $ = jsMind.$;

var css = function (cStyle, property_name) {
    return cStyle.getPropertyValue(property_name);
};
var is_visible = function (cStyle) {
    var visibility = css(cStyle, 'visibility');
    var display = css(cStyle, 'display');
    return visibility !== 'hidden' && display !== 'none';
};
var jcanvas = {};
jcanvas.rect = function (ctx, x, y, w, h, r) {
    if (w < 2 * r) r = w / 2;
    if (h < 2 * r) r = h / 2;
    ctx.moveTo(x + r, y);
    ctx.arcTo(x + w, y, x + w, y + h, r);
    ctx.arcTo(x + w, y + h, x, y + h, r);
    ctx.arcTo(x, y + h, x, y, r);
    ctx.arcTo(x, y, x + w, y, r);
};

jcanvas.text_multiline = function (ctx, text, x, y, w, h, lineheight) {
    var line = '';
    var text_len = text.length;
    var chars = text.split('');
    var test_line = null;
    ctx.textAlign = 'left';
    ctx.textBaseline = 'top';
    for (var i = 0; i < text_len; i++) {
        test_line = line + chars[i];
        if (ctx.measureText(test_line).width > w && i > 0) {
            ctx.fillText(line, x, y);
            line = chars[i];
            y += lineheight;
        } else {
            line = test_line;
        }
    }
    ctx.fillText(line, x, y);
};

jcanvas.text_ellipsis = function (ctx, text, x, y, w, h) {
    var center_y = y + h / 2;
    var text = jcanvas.fittingString(ctx, text, w);
    ctx.textAlign = 'left';
    ctx.textBaseline = 'middle';
    ctx.fillText(text, x, center_y, w);
};

jcanvas.fittingString = function (ctx, text, max_width) {
    var width = ctx.measureText(text).width;
    var ellipsis = '…';
    var ellipsis_width = ctx.measureText(ellipsis).width;
    if (width <= max_width || width <= ellipsis_width) {
        return text;
    } else {
        var len = text.length;
        while (width >= max_width - ellipsis_width && len-- > 0) {
            text = text.substring(0, len);
            width = ctx.measureText(text).width;
        }
        return text + ellipsis;
    }
};

jcanvas.image = function (ctx, url, x, y, w, h, r, rotation, callback) {
    var img = new Image();
    img.onload = function () {
        ctx.save();
        ctx.translate(x, y);
        ctx.save();
        ctx.beginPath();
        jcanvas.rect(ctx, 0, 0, w, h, r);
        ctx.closePath();
        ctx.clip();
        ctx.translate(w / 2, h / 2);
        ctx.rotate((rotation * Math.PI) / 180);
        ctx.drawImage(img, -w / 2, -h / 2);
        ctx.restore();
        ctx.restore();
        !!callback && callback();
    };
    img.src = url;
};

class screenshot {
    constructor(jm) {
        this.jm = jm;
        this.canvas_elem = null;
        this.canvas_ctx = null;
        this._inited = false;
    }
    init() {
        if (this._inited) {
            return;
        }
        console.log('init');
        var c = $.c('canvas');
        var ctx = c.getContext('2d');

        this.canvas_elem = c;
        this.canvas_ctx = ctx;
        this.jm.view.e_panel.appendChild(c);
        this._inited = true;
        this.resize();
    }
    shoot(callback) {
        this.init();
        this._draw(
            function () {
                !!callback && callback();
                this.clean();
            }.bind(this)
        );
        this._watermark();
    }
    shootDownload() {
        this.shoot(
            function () {
                this._download();
            }.bind(this)
        );
    }
    shootAsDataURL(callback) {
        this.shoot(
            function () {
                !!callback && callback(this.canvas_elem.toDataURL());
            }.bind(this)
        );
    }
    resize() {
        if (this._inited) {
            this.canvas_elem.width = this.jm.view.size.w;
            this.canvas_elem.height = this.jm.view.size.h;
        }
    }
    clean() {
        var c = this.canvas_elem;
        this.canvas_ctx.clearRect(0, 0, c.width, c.height);
    }
    _draw(callback) {
        var ctx = this.canvas_ctx;
        ctx.textAlign = 'left';
        ctx.textBaseline = 'top';
        this._draw_lines(
            function () {
                this._draw_nodes(callback);
            }.bind(this)
        );
    }
    _watermark() {
        var c = this.canvas_elem;
        var ctx = this.canvas_ctx;
        ctx.textAlign = 'right';
        ctx.textBaseline = 'bottom';
        ctx.fillStyle = '#000';
        ctx.font = '11px Verdana,Arial,Helvetica,sans-serif';
        ctx.fillText('hizzgdev.github.io/jsmind', c.width - 5.5, c.height - 2.5);
        ctx.textAlign = 'left';
        ctx.fillText($.w.location, 5.5, c.height - 2.5);
    }
    _draw_lines(callback) {
        this.jm.view.graph.copy_to(this.canvas_ctx, callback);
    }
    _draw_nodes(callback) {
        var nodes = this.jm.mind.nodes;
        var node;
        for (var nodeid in nodes) {
            node = nodes[nodeid];
            this._draw_node(node);
        }

        function check_nodes_ready() {
            console.log('check_node_ready' + new Date());
            var allOk = true;
            for (var nodeid in nodes) {
                node = nodes[nodeid];
                allOk = allOk & node.ready;
            }

            if (!allOk) {
                $.w.setTimeout(check_nodes_ready, 200);
            } else {
                $.w.setTimeout(callback, 200);
            }
        }
        check_nodes_ready();
    }
    _draw_node(node) {
        var ctx = this.canvas_ctx;
        var view_data = node._data.view;
        var node_element = view_data.element;
        var ncs = getComputedStyle(node_element);
        if (!is_visible(ncs)) {
            node.ready = true;
            return;
        }

        var bgcolor = css(ncs, 'background-color');
        var round_radius = parseInt(css(ncs, 'border-top-left-radius'));
        var color = css(ncs, 'color');
        var padding_left = parseInt(css(ncs, 'padding-left'));
        var padding_right = parseInt(css(ncs, 'padding-right'));
        var padding_top = parseInt(css(ncs, 'padding-top'));
        var padding_bottom = parseInt(css(ncs, 'padding-bottom'));
        var text_overflow = css(ncs, 'text-overflow');
        var font =
            css(ncs, 'font-style') +
            ' ' +
            css(ncs, 'font-variant') +
            ' ' +
            css(ncs, 'font-weight') +
            ' ' +
            css(ncs, 'font-size') +
            '/' +
            css(ncs, 'line-height') +
            ' ' +
            css(ncs, 'font-family');

        var rb = {
            x: view_data.abs_x,
            y: view_data.abs_y,
            w: view_data.width + 1,
            h: view_data.height + 1,
        };
        var tb = {
            x: rb.x + padding_left,
            y: rb.y + padding_top,
            w: rb.w - padding_left - padding_right,
            h: rb.h - padding_top - padding_bottom,
        };

        ctx.font = font;
        ctx.fillStyle = bgcolor;
        ctx.beginPath();
        jcanvas.rect(ctx, rb.x, rb.y, rb.w, rb.h, round_radius);
        ctx.closePath();
        ctx.fill();

        ctx.fillStyle = color;
        if ('background-image' in node.data) {
            var backgroundUrl = css(ncs, 'background-image').slice(5, -2);
            node.ready = false;
            var rotation = 0;
            if ('background-rotation' in node.data) {
                rotation = node.data['background-rotation'];
            }
            jcanvas.image(
                ctx,
                backgroundUrl,
                rb.x,
                rb.y,
                rb.w,
                rb.h,
                round_radius,
                rotation,
                function () {
                    node.ready = true;
                }
            );
        }
        if (!!node.topic) {
            if (text_overflow === 'ellipsis') {
                jcanvas.text_ellipsis(ctx, node.topic, tb.x, tb.y, tb.w, tb.h);
            } else {
                var line_height = parseInt(css(ncs, 'line-height'));
                jcanvas.text_multiline(ctx, node.topic, tb.x, tb.y, tb.w, tb.h, line_height);
            }
        }
        if (!!view_data.expander) {
            this._draw_expander(view_data.expander);
        }
        if (!('background-image' in node.data)) {
            node.ready = true;
        }
    }
    _draw_expander(expander) {
        var ctx = this.canvas_ctx;
        var ncs = getComputedStyle(expander);
        if (!is_visible(ncs)) {
            return;
        }

        var style_left = css(ncs, 'left');
        var style_top = css(ncs, 'top');
        var font = css(ncs, 'font');
        var left = parseInt(style_left);
        var top = parseInt(style_top);
        var is_plus = expander.innerHTML === '+';

        ctx.lineWidth = 1;

        ctx.beginPath();
        ctx.arc(left + 7, top + 7, 5, 0, Math.PI * 2, true);
        ctx.moveTo(left + 10, top + 7);
        ctx.lineTo(left + 4, top + 7);
        if (is_plus) {
            ctx.moveTo(left + 7, top + 4);
            ctx.lineTo(left + 7, top + 10);
        }
        ctx.closePath();
        ctx.stroke();
    }
    _download() {
        var c = this.canvas_elem;
        var name = this.jm.mind.name + '.png';

        if (navigator.msSaveBlob && !!c.msToBlob) {
            var blob = c.msToBlob();
            navigator.msSaveBlob(blob, name);
        } else {
            var bloburl = this.canvas_elem.toDataURL();
            var anchor = $.c('a');
            if ('download' in anchor) {
                anchor.style.visibility = 'hidden';
                anchor.href = bloburl;
                anchor.download = name;
                $.d.body.appendChild(anchor);
                var evt = $.d.createEvent('MouseEvents');
                evt.initEvent('click', true, true);
                anchor.dispatchEvent(evt);
                $.d.body.removeChild(anchor);
            } else {
                location.href = bloburl;
            }
        }
    }
    jm_event_handle(type, data) {
        if (type === jsMind.event_type.resize) {
            this.resize();
        }
    }
}

var screenshot_plugin = new jsMind.plugin('screenshot', function (jm) {
    var jss = new screenshot(jm);
    jm.screenshot = jss;
    jm.shoot = function () {
        jss.shoot();
    };
    jm.add_event_listener(function (type, data) {
        jss.jm_event_handle.call(jss, type, data);
    });
});

jsMind.register_plugin(screenshot_plugin);
